package view;

import java.util.zip.DataFormatException;

import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableColumn;

import server.Rule;
import server.RulesTableType;
import server.Rule.permissionOpt;
import server.Rule.typeOpt;

/**
 * Table with specified header - [index, IP, port, protocol, action]
 * for single rule storing.
 */
public class RuleTable extends JTable
{	
	/**
	 * Serial version ID - for serialization
	 */
	private static final long serialVersionUID = -2424212670113646511L;

	/**
	 * Column names
	 */
	protected final static String columnNames[] = 
		{"Id", "IP", "Port", "Protocol", "Action"};
	
	/**
	 * Index column index
	 */
	protected final static int idColumnIndex = 0;
	/**
	 * IP column index
	 */
	protected final static int ipColumnIndex = 1;
	/**
	 * Port column index
	 */
	protected final static int portColumnIndex = 2;
	/**
	 * Protocol column index
	 */
	protected final static int protocolColumnIndex = 3;
	/**
	 * Action column index
	 */
	protected final static int actionColumnIndex = 4;
	
	/**
	 * The table rows number
	 */
	private final static int rowNumber = 1;
	
	/**
	 * Column of cell with forbidded editing
	 */
	protected int irrelevantColumn = -1;
	
	/**
	 * Disabled column header
	 */
	protected final static String disabledColumnHeader = "N/A";
	
	/**
	 * C'tor - Table with specified columns and rows number is created and filled with default data.
	 */
	public RuleTable()
	{
		super(rowNumber, columnNames.length);
		
		for(int i = 0; i < columnNames.length; i++)
		{
			setValueAt("", 0, i);
		}
	}
	
	/**
	 * C'tor - Table with specified columns and rows number is created and filled with received rule data.
	 * @param rule - the rule data
	 */
	public RuleTable(String[] rule)
	{
		super(rowNumber, columnNames.length);
		
		for(int i = 0; i < columnNames.length && i < rule.length; i++)
		{
			setValueAt(rule[i], 0, i);
		}
	}

	/**
	 * Creates and returns table header.
	 * @return the header of the table
	 */
	public JTableHeader getRulesTableHeader()
	{		
		TableColumn column = null;
		for (int i = 0; i < columnNames.length; i++)
		{
		    column = getColumnModel().getColumn(i);
			column.setHeaderValue(columnNames[i]);
		}
		
		return getTableHeader();	
	}
	
	/**
	 * Creates rule from received data and table data.
	 * @param type		- the type of the rule
	 * @param direction	- the direction of the rule
	 * @return the rule with data from the table 
	 * @throws DataFormatException	- the exception is thrown when rule in the table filled in the wrong way.
	 */
	public Rule getRule(typeOpt type, RulesTableType direction) throws DataFormatException 
	{
		/* String for table line editing */
		String str = null;
		/* Rule creation */
		Rule rule = null;
		
		int index 		= -1;
		String iprange 	= null;
		Integer port   	= null;
		String protocol = null;
		permissionOpt permission = permissionOpt.DENY;
	
		/* Index */
		str = (String)getValueAt(0, idColumnIndex);
		try 
		{
			index = Integer.parseInt(str);
		}catch(NumberFormatException e){
			ErrorFrame.AddError("Wrong rule data format: index is not a valid number", 
								new JFrame());
			throw new DataFormatException();
		}
		
		/* Index check - not supposed to be more than number of defined rules */
		switch(direction)
		{
		case IN:
			if((index <= 0) || (index > Settings.inRulesNumber + 1))
			{
				ErrorFrame.AddError("Wrong rule data format: rule index is out of range.", 
									 new JFrame());
				throw new DataFormatException();
			}
			break;
		case OUT:
			if((index <= 0) || (index > Settings.outRulesNumber + 1))
			{
				ErrorFrame.AddError("Wrong rule data format: rule index is out of range.", 
									 new JFrame());
				throw new DataFormatException();
			}
			break;	
		}

		/* Action */		
	    str = (String) this.getValueAt(0, actionColumnIndex);
		if (str.equals(permissionOpt.DENY.name()))
		{
			permission = Rule.permissionOpt.DENY;
		}
		else if (str.equals(permissionOpt.ALLOW.name()) )
		{
			permission = Rule.permissionOpt.ALLOW;
		}else
		{
			ErrorFrame.AddError("Wrong data format:action has to be \"ALLOW\" or \"DENY\"",
								new JFrame());
			throw new DataFormatException();
		}
		
		switch(type)
		{
		case CONTEXT:
			/* Port */
	        str = (String)this.getValueAt(0, portColumnIndex);
	    	if (str == null ||  str.equals(""))
	    	{
				/* Port is required field */
	    		ErrorFrame.AddError("Wrong rule data format: port isn't specified.", 
						 			new JFrame());
	    		throw new DataFormatException();
	    	}
	    	else
	    	{
	    		try
	            {
	        		port = Integer.parseInt(str);
	            }catch(NumberFormatException e){
					/* Port is natural number */
	    			ErrorFrame.AddError("Wrong rule data format: port is not a valid number", 
										 new JFrame());
	    			throw new DataFormatException();
	    		}
	    	}
			
			/* Protocol */
			str = (String) this.getValueAt(0, protocolColumnIndex);
			if (str == null ||  str.equals(""))
			{
				/* Protocol is required field */
				ErrorFrame.AddError("Wrong rule format: protocol isn't specified.",
									new JFrame());
    			throw new DataFormatException();
			}
			else
			{
				protocol = str;
			}
			
			rule = new Rule(index, port, protocol, permission);
			break;
		case IP:
			/* IP range */
	        str = (String)getValueAt(0, ipColumnIndex);;
	        if (str == null ||  str.equals(""))
	        {
				/* IP isn't required field */
	        	iprange = null;
	    	}
	        else
	        {
	        	iprange  = str;
	        }
	        
			/* Port */
	        str = (String) this.getValueAt(0, portColumnIndex);
	    	if (str == null ||  str.equals(""))
	    	{
	    		port = null;
	    	}
	    	else
	    	{
	    		try
	            {
					/* Port isn't required field */
	        		port = Integer.parseInt(str);
	            }catch(NumberFormatException e){
					/* Port is natural number */
	    			ErrorFrame.AddError("Wrong rule data format: port is not a valid number", 
										 new JFrame());
	    			throw new DataFormatException();
	    		}
	    	}
	    	
	    	/* Rule creation */
			rule = new Rule(index, iprange, port, permission);
			break;
		}

		return  rule;
	}
	
	/**
	 * Fill the table with received data.
	 * @param rule - the rule data
	 */
	public void setRule(String[] rule)
	{		
		for(int i = 0; i < columnNames.length && i < rule.length; i++)
		{
			setValueAt(rule[i], 0, i);
		}
	}
	
	/**
	 * Chenge the title of the disabled column to N/A
	 * @param column - the column of the cell to disable
	 */
	public void setDisabled(int column)
	{
		irrelevantColumn = column;
		
		for (int i = 0; i < columnNames.length; i++)
		{
			if(column == i)
			{
				getColumnModel().getColumn(i).setHeaderValue(disabledColumnHeader);				
			}
			else
			{
				getColumnModel().getColumn(i).setHeaderValue(columnNames[i]);				
			}
		}

		getColumnModel().getColumn(RuleTable.protocolColumnIndex).setWidth(0);
	}
	
	/**
	 * Make all cells except for irrelevant one to disabled.
	 * @param row		- row of the cell
	 * @param column	- column of the cell
	 * @return flag if the cell is editable
	 */
	public boolean isCellEditable(int row, int column) 
	{
		if(column == this.irrelevantColumn)
		{
			return false;
		}
		
		return true;
	}
}
